(feat): reserved blockspace#2309
Draft
manav2401 wants to merge 23 commits into
Draft
Conversation
…+ hardfork gate (POS-3637)
…-3573) Reserved-blockspace senders (post-fork, classified via BorConfig) execute fee-free: no gas debit at buyGas, the EIP-1559 fee-cap floor is waived in preCheck, no gas refund, no producer tip, and no base-fee burn. The decision is computed once in newStateTransition so the serial and BlockSTM paths agree on the same state root. Fee fields stay readable for off-chain settlement.
Post-ReservedBlockspace, CalcBaseFee holds the reserved capacity out of the gas target and nets the parent's ReservedGasUsed out of gas used, so the public base fee tracks only normal-region demand against normal-region capacity. Anchoring the target on reserved capacity (Sigma quotas) rather than reserved used gas keeps the public market stable and stops a reserved client from moving the public fee with its own usage. Header.GasLimit is untouched (formula-only input). VerifyEIP1559Header inherits the change: its pre-Lisovo path recomputes via CalcBaseFee and its post-Lisovo path is a bounded check.
…-3570) Reserved-blockspace senders pay zero in-protocol fee, but the pool rejected and dropped their txs in several places. Add a sender-based, consensus-uniform reserved branch (the set comes from the chain config, matching the EVM and base-fee paths): - validation.go: waive the admission tip floor for reserved senders. - legacypool Pending: don't cap reserved senders' txs by the miner tip filter, so they reach the producer. - list.Add: reserved senders replace by arrival order (skip the price-bump rule) so a stuck zero-fee tx can be cancelled or replaced. The balance check needs no change: a zero-feeCap tx's Cost() is already just its value. Sender classification is gated on the ReservedBlockspace fork height.
Factor the reserved branches out of the EVM and consensus hot paths into focused helpers (reservedZeroFeeGas, calcBaseFeeBurn, verifyReservedFields). Behaviour is unchanged; this keeps buyGas, execute, and verifyHeader within the cognitive- complexity budget after the reserved-blockspace additions.
…(POS-3637) Pins the hardfork-rollout requirement that a scheduled fork prints its activation height on startup, and that an unscheduled (nil) fork stays silent.
…cks (POS-3570) The pool admitted reserved zero-fee txs but the producer dropped them: the price-and-nonce ordering rejects any tx with GasFeeCap below the base fee (ErrGasFeeCapTooLow). Carry the pool's reserved classification on LazyTransaction and exempt reserved senders from the base-fee floor in newTxWithMinerFee (zero miner tip). Caught by a two-node produce/verify integration test (tests/bor, integration tag): a reserved zero-fee tx is now mined and cross-verified, the sender pays only its call value, and a non-reserved zero-fee tx is still rejected at admission.
…(POS-3570) Review surfaced that several comments described not-yet-implemented work as if it were live. Correct them so the foundation's deferrals read honestly; no logic change: - verifyReservedFields: quota/count value-correctness is enforced by block validation in POS-3576, not today — a producer can write any present values. - setReservedBlockspaceExtraFields: the zero ReservedTxCount/ReservedGasUsed are placeholders; the producer's reserved pass (POS-3575) sets the real values. - CalcBaseFee: the capacity-side target reduction is live while the ReservedGasUsed used-side netting stays inert until POS-3575 populates the header field, so the public base fee runs slightly hot under reserved load. Deterministic (a fee-accuracy gap, not a consensus split); resolves with POS-3575, no code change needed here. - buyGas: note the zero-fee waiver also drops a reserved blob tx's blob fee; reserved clients are not a blob use case today.
… onto the foundation branch Integrates Krishang's reserved-blockspace registry (ReservedBlockspaceRegistry.sol + Go reader/registryreader + genesis embed + txpool/miner/core wiring) alongside our consumer-side foundation (zero-fee EVM, normal-region base fee, txpool/miner admission, header fields). Clean three-way merge; whole repo builds and both the reserved-consumer and registry test suites pass. Post-merge the registry (source of truth) and our config-stub classifier still run as parallel mechanisms; unifying the consumers onto the registry is the next step. Contract still needs root()/effectiveFrom/feeMode per the finalized spec.
…kspace registry (POS-3572) Extend Krishang's ReservedBlockspaceRegistry per the finalized spec (design.md §4, §7): - feeMode (0=free, 1=routed) per client + setClientFeeMode; surfaced via getClientForAddress. - effectiveFrom block per client + setClientEffectiveFrom; Bor gates on active && effectiveFrom <= number (read at parent state, so deterministic). - configVersion + root(): a change-epoch bumped on every mutation, so Bor can cache its reserved-set snapshot keyed on root() and only rebuild when it moves (spec §4.5), avoiding a per-tx state read. Regenerate the embedded runtime bytecode (forge, solc 0.8.33 pinned), update the read-only ABI (getClientForAddress now 6 returns + root), and sync the Go reader (decode feeMode/effectiveFrom, add Root()) + registryreader.ClientLookup/Reader. 11 foundry tests and the Go registry tests pass.
Introduce registryreader.Snapshot — an immutable, pure-lookup view of the reserved set built once from the registry at a given block state (spec §4.5), so the hot classification paths (txpool admission, EVM fee-skip, base-fee capacity) never do a per-transaction state read. BuildSnapshot reads the active whitelist + per-client feeMode/effectiveFrom/quota + capacity + root(); callers cache it and rebuild only when root() moves. nil-safe (no registry = classifies nothing). Extends the Reader interface with WhitelistedAddresses/TotalReservedGas (already implemented on the concrete reader and the test harness). Snapshot unit test builds from the real contract bytecode via the harness and verifies classification, feeMode, capacity, and root.
…fig (POS-3570/3574) Repoint the pool's reserved classification off the config stub onto the registry: legacypool builds a registryreader.Snapshot at each reset from the new head's state, and isReserved / validation.go's tip-floor waiver query the snapshot (rebuilt per head, not per tx — spec §4.5). The fork-height gate stays in chain config (a legit chain param); only the reserved *set* now comes from the registry. TxPool.SetReservedRegistry propagates the reader to subpools. Reserved unit tests now drive a fake registryreader.Reader as the source; full txpool suite green.
… in execution (POS-3570/3574) Build a per-block reserved-set snapshot from the registry at parent state and inject it into the EVM block context across the serial, V1-parallel, and V2 BlockSTM execution paths plus the miner. newStateTransition now classifies reserved senders from the snapshot instead of the config stub, keeping serial and parallel execution byte-identical.
…stub CalcBaseFee is pure (config, parent) with no parent state at most call sites, so the reserved-capacity input cannot read the registry here; it arrives via a producer-stamped header field (POS-3575/3576), mirroring the inert reserved-gas-used half. Records the decision so the retained config stub is deliberate, not an oversight.
…nsensus-safe (POS-3574) A registry-initialized two-miner devnet exposed two ways reserved-blockspace classification could differ between the block producer and the verifying nodes, each a consensus split: - BuildSnapshot read the registry through the EVM (ApplyMessage), which mutates the statedb it runs against. On the execution path the caller passes the live block state, so the read leaked gas/nonce/touch changes into the block and diverged the state root. Read against a throwaway state copy. - The serial and V2 block processors and the prefetcher only hold a *HeaderChain context, which did not expose the registry reader, so ReservedSnapshotForBlock fell through its type assertion to a nil snapshot on every import path. Producers (full chain) classified reserved senders and built fee-free blocks that verifiers then rejected as underpriced. Mirror the reader onto HeaderChain so produce and verify share one source. Also gate the execution-path snapshot build on the fork height so nodes syncing from genesis skip the per-block copy and registry read before the reserved fork activates. Adds a registryreader unit test (Snapshot accessors, the effectiveFrom activation boundary, BuildSnapshot error/nil propagation) and converts the end-to-end integration test to seed the registry via real initialize()/createClient() txs, asserting a reserved zero-fee tx is admitted, mined fee-free, and accepted by the peer (cross-node parity), while a non-reserved zero-fee tx is rejected at admission.
…s (POS-3574) Multi-agent pre-PR review (/pos-code-review) surfaced fixes worth taking now; the rest are tracked as pre-activation work in the run report. - ethapi: bypass the EVM wall-clock timeout for bor-internal system-contract reads, not just the gas cap. The reserved-registry snapshot read runs through doCall, whose RPCEVMTimeout is node-local and load-dependent. A timeout on one node but not another returned a nil snapshot and flipped fee classification, diverging the state root. Both node-local limits are now bypassed together for internal consensus reads (validators, span, state receiver, registry). - params: reject scheduling ReservedBlockspaceBlock before CancunBlock (the reserved header fields only encode in the post-Cancun extra-data format, while verifyHeader requires them once the fork is active), or without a registry contract configured. Prevents a rollout height that bricks block production. - bor: add an interim header-only bound (ReservedGasUsed <= block GasUsed) in verifyReservedFields, rejecting an impossible value the base fee would otherwise silently clamp. Full value-correctness stays with POS-3576. - types: GetReservedInfo now routes through DecodeBlockExtraData instead of re-decoding the extra blob independently. - params: drop the dead config-stub classifiers (IsReservedSender/ ReservedQuotaOf — no production caller; classification is registry-sourced) and document that ReservedClients now feeds only the base-fee capacity carve-out until the producer-stamped header field lands. - registryreader: correct the Snapshot/Root/BuildSnapshot docs — the root()-keyed cross-block cache is a tracked optimization, not yet implemented on the execution path (txpool caches per head; execution rebuilds per block). Adds tests for the Cancun-order guard, the ReservedGasUsed bound, and the reserved base-fee capacity-equals-limit boundary.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
More changes for reserved blockspace
Still WIP
Executed tests
<what was actually run beyond CI's standard unit / integration / e2e gates: kurtosis scenarios, chaos runs, manual checks against Amoy / mainnet RPCs, devnet upgrades, etc. Include output or pointers to where the run lives.>
Rollout notes
<consensus-affecting? requires coordinated upgrade? backwards-compatible? operator-facing change?>